package com.yungu.swift.api.base.handler;

import com.yungu.swift.api.base.Constants;
import com.yungu.swift.api.base.gateway.GatewayHeader;
import com.yungu.swift.api.config.ApplicationConfig;
import com.yungu.swift.api.utils.RsaUtil;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.utils.JsonUtils;
import com.yungu.swift.utils.Md5Utils;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.*;
import java.util.*;

import static com.yungu.swift.api.base.ApiErrorEnum.SIGN_ERROR;

/**
 * @author : cuixiuyin
 * @date : 2019/10/28
 */
@Slf4j
public class SignHandler extends HandlerInterceptorAdapter {

    /**
     * 签名加密密钥
     */
    private final static String AUTH_KEY = "fffa2a879e62e198924a95dc150dc33823bd0bf7abd3a20fff605f297ae9333804699723a6219048";
    private final static String GET = "GET";
    private final static String POST = "POST";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        String encodeMd5 = "";
        String method = request.getMethod();
        //请求是JSON格式数据
        if (POST.equals(method) && request instanceof HttpServletRequestWrapper) {
            HttpServletRequestWrapper requestWrapper = (HttpServletRequestWrapper) request;
            String bodyStr = getBodyString(requestWrapper.getReader());
            encodeMd5 = signJsonParam(request.getHeader(GatewayHeader.noncestr), request.getHeader(GatewayHeader.appid), bodyStr);
        }
        //请求是文件流的数据
        if (POST.equals(method) && (request.getContentType().contains(MediaType.MULTIPART_FORM_DATA_VALUE)
                || request.getContentType().contains("multipart/mixed"))) {
            Collection<Part> parts = request.getParts();
            Map<String, String[]> paramMap = new HashMap<>(8);
            for (Part part : parts) {
                if (part.getContentType() != null) {
                    paramMap.put(part.getName(), new String[]{Md5Utils.encode(part.getInputStream())});
                    continue;
                }
                paramMap.put(part.getName(), new String[]{stream2Str(part.getInputStream())});
            }
            encodeMd5 = signFormParam(request.getHeader(GatewayHeader.noncestr), paramMap);
        }
        if (GET.equals(method)) {
            Map<String, String[]> paramMap = request.getParameterMap();
            encodeMd5 = signFormParam(request.getHeader(GatewayHeader.noncestr), paramMap);
        }
        String sign = request.getHeader(GatewayHeader.sign);
        log.info("----------url:{}", request.getRequestURI());
        log.info("---------sign:{}", sign);
        log.info("---------encodeMd5:{}", encodeMd5);
        if (!sign.equals(encodeMd5)) {
            outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(SIGN_ERROR.getCode(), SIGN_ERROR.getMsg() + ":" + encodeMd5)));
            return false;
        }
        response.reset();
        return super.preHandle(request, response, handler);
    }


    /**
     * application/json 参数加签
     *
     * @param noncestr
     * @param appid
     * @param bodyStr
     * @return
     */

    private String signJsonParam(String noncestr, String appid, String bodyStr) {
        Map<String, Object> content = new LinkedHashMap<>(8);
        content.put(GatewayHeader.appid, appid);
        content.put(GatewayHeader.noncestr, noncestr);
        content.put("key", AUTH_KEY);
        String encodeMd5 = Md5Utils.encode(JsonUtils.toJSONString(content) + bodyStr);
        log.info("---------content:{},bodyStr:{}", JsonUtils.toJSONString(content), bodyStr);
        return encodeMd5;
    }


    /**
     * application/form-date、 get url 参数加签
     *
     * @param noncestr
     * @param paramMap
     * @return
     */
    private String signFormParam(String noncestr, Map<String, String[]> paramMap) {
        paramMap.put(GatewayHeader.noncestr, new String[]{noncestr});
        List<String> list = new ArrayList<>(paramMap.keySet());
        Collections.sort(list);
        String encodeContent = "";
        for (int i = 0; i < list.size(); i++) {
            String paramVal = paramMap.get(list.get(i))[0];
            if (StringUtils.isNotEmpty(paramVal)) {
                encodeContent += list.get(i) + "=" + StringUtils.stripToEmpty(paramVal) + "&";
            }
        }
        encodeContent = encodeContent + "key=" + Constants.AUTH_KEY;
        log.info("---------encodeContent:{}", encodeContent);
        return Md5Utils.encode(encodeContent);
    }

    private String getBodyString(BufferedReader br) throws IOException {
        StringBuilder builder = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            builder.append(line);
        }
        br.close();
        return builder.toString();
    }

    private String stream2Str(InputStream inputStream) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
            String line;
            StringBuilder builder = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }
            return builder.toString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    private void outPrint(PrintWriter out, String jsonstr) {
        if (ApplicationConfig.SERVER_CONFIG.getSafety().getEncrypt()) {
            out.print(RsaUtil.doUrlAndEncrypt(jsonstr));
        } else {
            out.print(jsonstr);
        }
    }
}
